package com.aisino.nmg.mybatis.generator;

import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.internal.util.StringUtility;

import java.text.SimpleDateFormat;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Properties;
import java.util.Set;

import static org.mybatis.generator.internal.util.StringUtility.isTrue;

public class CustomCommentGenerator implements CommentGenerator {

	private Properties properties;

	private boolean suppressDate;

	private boolean suppressAllComments;

	private boolean addRemarkComments;

	private SimpleDateFormat dateFormat;

	public CustomCommentGenerator() {
		super();
		properties = new Properties();
		suppressDate = false;
		suppressAllComments = false;
		addRemarkComments = false;
	}

	@Override
	public void addJavaFileComment(CompilationUnit compilationUnit) {
		// add no file level comments by default
	}

	/**
	 * Adds a suitable comment to warn users that the element was generated, and when it was generated.
	 *
	 * @param xmlElement
	 *            the xml element
	 */
	@Override
	public void addComment(org.mybatis.generator.api.dom.xml.XmlElement xmlElement) {
//		if (suppressAllComments) {
//			return;
//		}
//
//		xmlElement.addElement(new TextElement("<!--")); //$NON-NLS-1$
//
//		StringBuilder sb = new StringBuilder();
//		sb.append("  WARNING - "); //$NON-NLS-1$
//		//sb.append(MergeConstants.NEW_ELEMENT_TAG);
//		//xmlElement.addElement(new TextElement(sb.toString()));
//		String s = getDateString();
//		if (s != null) {
//			sb.setLength(0);
//			sb.append("  This element was generated on "); //$NON-NLS-1$
//			sb.append(s);
//			sb.append('.');
//			xmlElement.addElement(new TextElement(sb.toString()));
//		}
//
//		xmlElement.addElement(new TextElement("-->")); //$NON-NLS-1$
	}

	@Override
	public void addRootComment(XmlElement rootElement) {
		// add no document level comments by default
	}

	@Override
	public void addConfigurationProperties(Properties properties) {
		this.properties.putAll(properties);

		suppressDate = isTrue(properties
				                      .getProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_DATE));

		suppressAllComments = isTrue(properties
				                             .getProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_ALL_COMMENTS));

		addRemarkComments = isTrue(properties
				                           .getProperty(PropertyRegistry.COMMENT_GENERATOR_ADD_REMARK_COMMENTS));

		String dateFormatString = properties.getProperty(PropertyRegistry.COMMENT_GENERATOR_DATE_FORMAT);
		if (StringUtility.stringHasValue(dateFormatString)) {
			dateFormat = new SimpleDateFormat(dateFormatString);
		}
	}

	/**
	 * This method adds the custom javadoc tag for. You may do nothing if you do not wish to include the Javadoc tag -
	 * however, if you do not include the Javadoc tag then the Java merge capability of the eclipse plugin will break.
	 *
	 * @param javaElement
	 *            the java element
	 * @param markAsDoNotDelete
	 *            the mark as do not delete
	 */
	protected void addJavadocTag(JavaElement javaElement,
	                             boolean markAsDoNotDelete) {
//		javaElement.addJavaDocLine(" *"); //$NON-NLS-1$
//		StringBuilder sb = new StringBuilder();
//		sb.append(" * "); //$NON-NLS-1$
//		sb.append(MergeConstants.NEW_ELEMENT_TAG);
//		if (markAsDoNotDelete) {
//			sb.append(" do_not_delete_during_merge"); //$NON-NLS-1$
//		}
//		String s = getDateString();
//		if (s != null) {
//			sb.append(' ');
//			sb.append(s);
//		}
//		javaElement.addJavaDocLine(sb.toString());
	}

	/**
	 * Returns a formated date string to include in the Javadoc tag
	 * and XML comments. You may return null if you do not want the date in
	 * these documentation elements.
	 *
	 * @return a string representing the current timestamp, or null
	 */
	protected String getDateString() {
		if (suppressDate) {
			return null;
		} else if (dateFormat != null) {
			return dateFormat.format(new Date());
		} else {
			return new Date().toString();
		}
	}

	@Override
	public void addClassComment(InnerClass innerClass,
	                            IntrospectedTable introspectedTable) {
		if (suppressAllComments) {
			return;
		}

		StringBuilder sb = new StringBuilder();

		innerClass.addJavaDocLine("/**"); //$NON-NLS-1$
		innerClass
				.addJavaDocLine(" * This class was generated by MyBatis Generator."); //$NON-NLS-1$

		sb.append(" * This class corresponds to the database table "); //$NON-NLS-1$
		sb.append(introspectedTable.getFullyQualifiedTable());
		innerClass.addJavaDocLine(sb.toString());

		addJavadocTag(innerClass, false);

		innerClass.addJavaDocLine(" */"); //$NON-NLS-1$
	}

	@Override
	public void addClassComment(InnerClass innerClass,
	                            IntrospectedTable introspectedTable, boolean markAsDoNotDelete) {
		if (suppressAllComments) {
			return;
		}

		StringBuilder sb = new StringBuilder();

		innerClass.addJavaDocLine("/**"); //$NON-NLS-1$
		innerClass
				.addJavaDocLine(" * This class was generated by MyBatis Generator."); //$NON-NLS-1$

		sb.append(" * This class corresponds to the database table "); //$NON-NLS-1$
		sb.append(introspectedTable.getFullyQualifiedTable());
		innerClass.addJavaDocLine(sb.toString());

		addJavadocTag(innerClass, markAsDoNotDelete);

		innerClass.addJavaDocLine(" */"); //$NON-NLS-1$
	}

	@Override
	public void addModelClassComment(TopLevelClass topLevelClass,
	                                 IntrospectedTable introspectedTable) {
		if (suppressAllComments  || !addRemarkComments) {
			return;
		}

		topLevelClass.addJavaDocLine("/**"); //$NON-NLS-1$

		String remarks = introspectedTable.getRemarks();
		if (addRemarkComments && StringUtility.stringHasValue(remarks)) {
			topLevelClass.addJavaDocLine(" * Database Table Remarks:"); //$NON-NLS-1$
			String[] remarkLines = remarks.split(System.getProperty("line.separator"));  //$NON-NLS-1$
			for (String remarkLine : remarkLines) {
				topLevelClass.addJavaDocLine(" *   " + remarkLine);  //$NON-NLS-1$
			}
		}
		topLevelClass.addJavaDocLine(" *"); //$NON-NLS-1$

		topLevelClass
				.addJavaDocLine(" * This class was generated by MyBatis Generator."); //$NON-NLS-1$

		StringBuilder sb = new StringBuilder();
		sb.append(" * This class corresponds to the database table "); //$NON-NLS-1$
		sb.append(introspectedTable.getFullyQualifiedTable());
		topLevelClass.addJavaDocLine(sb.toString());

		topLevelClass.addJavaDocLine(" */"); //$NON-NLS-1$
	}

	@Override
	public void addEnumComment(InnerEnum innerEnum,
	                           IntrospectedTable introspectedTable) {
		if (suppressAllComments) {
			return;
		}

		StringBuilder sb = new StringBuilder();

		innerEnum.addJavaDocLine("/**"); //$NON-NLS-1$
		innerEnum
				.addJavaDocLine(" * This enum was generated by MyBatis Generator."); //$NON-NLS-1$

		sb.append(" * This enum corresponds to the database table "); //$NON-NLS-1$
		sb.append(introspectedTable.getFullyQualifiedTable());
		innerEnum.addJavaDocLine(sb.toString());

		addJavadocTag(innerEnum, false);

		innerEnum.addJavaDocLine(" */"); //$NON-NLS-1$
	}

	@Override
	public void addFieldComment(Field field,
	                            IntrospectedTable introspectedTable,
	                            IntrospectedColumn introspectedColumn) {
		field.addJavaDocLine("/**");
		field.addJavaDocLine(" * @Fields "+field.getName()+" "+introspectedColumn.getRemarks());
		field.addJavaDocLine(" */");
	}

	@Override
	public void addFieldComment(Field field, IntrospectedTable introspectedTable) {
		if (suppressAllComments) {
			return;
		}

		StringBuilder sb = new StringBuilder();

		field.addJavaDocLine("/**"); //$NON-NLS-1$
		field
				.addJavaDocLine(" * This field was generated by MyBatis Generator."); //$NON-NLS-1$

		sb.append(" * This field corresponds to the database table "); //$NON-NLS-1$
		sb.append(introspectedTable.getFullyQualifiedTable());
		field.addJavaDocLine(sb.toString());

		addJavadocTag(field, false);

		field.addJavaDocLine(" */"); //$NON-NLS-1$
	}

	@Override
	public void addGeneralMethodComment(Method method,
	                                    IntrospectedTable introspectedTable) {
//		if (suppressAllComments) {
//			return;
//		}
//
//		StringBuilder sb = new StringBuilder();
//
//		method.addJavaDocLine("/**"); //$NON-NLS-1$
//		method
//				.addJavaDocLine(" * This method was generated by MyBatis Generator."); //$NON-NLS-1$
//
//		sb.append(" * This method corresponds to the database table "); //$NON-NLS-1$
//		sb.append(introspectedTable.getFullyQualifiedTable());
//		method.addJavaDocLine(sb.toString());
//
//		addJavadocTag(method, false);
//
//		method.addJavaDocLine(" */"); //$NON-NLS-1$
	}

	@Override
	public void addGetterComment(Method method,
	                             IntrospectedTable introspectedTable,
	                             IntrospectedColumn introspectedColumn) {
//		if (suppressAllComments) {
//			return;
//		}
//
//		StringBuilder sb = new StringBuilder();
//
//		method.addJavaDocLine("/**"); //$NON-NLS-1$
//		sb.append(introspectedTable.getFullyQualifiedTable());
//		sb.append('.');
//		sb.append(introspectedColumn.getActualColumnName());
//		method.addJavaDocLine(sb.toString());
//
//		method.addJavaDocLine(" *"); //$NON-NLS-1$
//
//		sb.setLength(0);
//		sb.append(" * @return the value of "); //$NON-NLS-1$
//		sb.append(introspectedTable.getFullyQualifiedTable());
//		sb.append('.');
//		sb.append(introspectedColumn.getActualColumnName());
//		method.addJavaDocLine(sb.toString());
//
//		addJavadocTag(method, false);
//
//		method.addJavaDocLine(" */"); //$NON-NLS-1$
	}

	@Override
	public void addSetterComment(Method method,
	                             IntrospectedTable introspectedTable,
	                             IntrospectedColumn introspectedColumn) {
//		if (suppressAllComments) {
//			return;
//		}
//
//		StringBuilder sb = new StringBuilder();
//
//		method.addJavaDocLine("/**"); //$NON-NLS-1$
//		sb.append(introspectedTable.getFullyQualifiedTable());
//		sb.append('.');
//		sb.append(introspectedColumn.getActualColumnName());
//		method.addJavaDocLine(sb.toString());
//
//		method.addJavaDocLine(" *"); //$NON-NLS-1$
//
//		Parameter parm = method.getParameters().get(0);
//		sb.setLength(0);
//		sb.append(" * @param "); //$NON-NLS-1$
//		sb.append(parm.getName());
//		sb.append(" the value for "); //$NON-NLS-1$
//		sb.append(introspectedTable.getFullyQualifiedTable());
//		sb.append('.');
//		sb.append(introspectedColumn.getActualColumnName());
//		method.addJavaDocLine(sb.toString());
//
//		addJavadocTag(method, false);
//
//		method.addJavaDocLine(" */"); //$NON-NLS-1$
	}

	@Override
	public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable,
	                                       Set<FullyQualifiedJavaType> imports) {
		imports.add(new FullyQualifiedJavaType("javax.annotation.Generated")); //$NON-NLS-1$
		String comment = "Source Table: " + introspectedTable.getFullyQualifiedTable().toString(); //$NON-NLS-1$
		method.addAnnotation(getGeneratedAnnotation(comment));
	}

	@Override
	public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable,
	                                       IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {
		imports.add(new FullyQualifiedJavaType("javax.annotation.Generated")); //$NON-NLS-1$
		String comment = "Source field: " //$NON-NLS-1$
		                 + introspectedTable.getFullyQualifiedTable().toString()
		                 + "." //$NON-NLS-1$
		                 + introspectedColumn.getActualColumnName();
		method.addAnnotation(getGeneratedAnnotation(comment));
	}

	@Override
	public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable,
	                               Set<FullyQualifiedJavaType> imports) {
		imports.add(new FullyQualifiedJavaType("javax.annotation.Generated")); //$NON-NLS-1$
		String comment = "Source Table: " + introspectedTable.getFullyQualifiedTable().toString(); //$NON-NLS-1$
		field.addAnnotation(getGeneratedAnnotation(comment));
	}

	@Override
	public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable,
	                               IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {
		imports.add(new FullyQualifiedJavaType("javax.annotation.Generated")); //$NON-NLS-1$
		String comment = "Source field: " //$NON-NLS-1$
		                 + introspectedTable.getFullyQualifiedTable().toString()
		                 + "." //$NON-NLS-1$
		                 + introspectedColumn.getActualColumnName();
		field.addAnnotation(getGeneratedAnnotation(comment));

		if (!suppressAllComments && addRemarkComments) {
			String remarks = introspectedColumn.getRemarks();
			if (addRemarkComments && StringUtility.stringHasValue(remarks)) {
				field.addJavaDocLine("/**"); //$NON-NLS-1$
				field.addJavaDocLine(" * Database Column Remarks:"); //$NON-NLS-1$
				String[] remarkLines = remarks.split(System.getProperty("line.separator"));  //$NON-NLS-1$
				for (String remarkLine : remarkLines) {
					field.addJavaDocLine(" *   " + remarkLine);  //$NON-NLS-1$
				}
				field.addJavaDocLine(" */"); //$NON-NLS-1$
			}
		}
	}

	@Override
	public void addClassAnnotation(InnerClass innerClass, IntrospectedTable introspectedTable,
	                               Set<FullyQualifiedJavaType> imports) {
		imports.add(new FullyQualifiedJavaType("javax.annotation.Generated")); //$NON-NLS-1$
		String comment = "Source Table: " + introspectedTable.getFullyQualifiedTable().toString(); //$NON-NLS-1$
		innerClass.addAnnotation(getGeneratedAnnotation(comment));
	}

	private String getGeneratedAnnotation(String comment) {
		StringBuilder buffer = new StringBuilder();
		buffer.append("@Generated("); //$NON-NLS-1$
		if (suppressAllComments) {
			buffer.append('\"');
		} else {
			buffer.append("value=\""); //$NON-NLS-1$
		}

		buffer.append(MyBatisGenerator.class.getName());
		buffer.append('\"');

		if (!suppressDate && !suppressAllComments) {
			buffer.append(", date=\""); //$NON-NLS-1$
			buffer.append(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now()));
			buffer.append('\"');
		}

		if (!suppressAllComments) {
			buffer.append(", comments=\""); //$NON-NLS-1$
			buffer.append(comment);
			buffer.append('\"');
		}

		buffer.append(')');
		return buffer.toString();
	}
}
